/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.apache.geode.internal.datasource;

import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.geode.annotations.Immutable;
import org.apache.geode.logging.internal.log4j.api.LogService;

/**
 * AbstractDataSource implements the Datasource interface. This is base class for the datasouce
 * types. The class also implements the Serializable and Referenceable behavior.
 *
 * This class now contains only those paramaters which are needed by the Gemfire DataSource
 * configuration. This maps to those paramaters which are specified as attributes of
 * <jndi-binding>tag. Those parameters which are specified as attributes of <property>tag are not
 * stored.
 */
public abstract class AbstractDataSource implements Serializable, DataSource, AutoCloseable {

  @Immutable
  private static final org.apache.logging.log4j.Logger logger = LogService.getLogger();

  protected transient PrintWriter dataSourcePW;
  protected int loginTimeOut;
  protected String user;
  protected String password;
  protected String serverName;
  protected String url;
  protected String jdbcDriver;
  protected ConfiguredDataSourceProperties configProps;
  protected boolean isActive = true;

  /**
   * Constructor for the AbstractDataSource.
   *
   * @param configs ConfiguredDataSourceProperties object containing all the data required to
   *        construct the datasource object.
   *
   */
  public AbstractDataSource(ConfiguredDataSourceProperties configs) throws SQLException {
    loginTimeOut = configs.getLoginTimeOut();
    user = configs.getUser();
    password = configs.getPassword();
    url = configs.getURL();
    jdbcDriver = configs.getJDBCDriver();
    dataSourcePW = configs.getPrintWriter();
    isActive = true;
  }

  /**
   * Behavior from the dataSource interface. This is an abstract function which will implemented in
   * the Datasource classes.
   *
   * @return Connection Connection object for the Database connection.
   */
  @Override
  public abstract Connection getConnection() throws SQLException;

  /**
   * Behavior from the dataSource interface. This is an abstract function which will be implemented
   * in the Datasource classes.
   *
   * @param username Username used for making database connection.
   * @param password Passowrd used for making database connection.
   * @return ???
   */
  @Override
  public abstract Connection getConnection(String username, String password) throws SQLException;

  // DataSource Interface functions
  /**
   * Returns the writer for logging
   *
   * @return PrintWriter for logs.
   */
  @Override
  public PrintWriter getLogWriter() throws SQLException {
    return dataSourcePW;
  }

  /**
   * Returns the amount of time that login will wait for the connection to happen before it gets
   * time out.
   *
   * @return int login time out.
   */
  @Override
  public int getLoginTimeout() throws SQLException {
    return loginTimeOut;
  }

  /**
   * sets the log writer for the datasource.
   *
   * @param out PrintWriter for writing the logs.
   */
  @Override
  public void setLogWriter(java.io.PrintWriter out) throws SQLException {
    dataSourcePW = out;
  }

  /**
   * Sets the login time out for the database connection
   *
   * @param seconds login time out in seconds.
   */
  @Override
  public void setLoginTimeout(int seconds) throws SQLException {
    loginTimeOut = seconds;
  }

  @Override
  public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    throw new SQLFeatureNotSupportedException();
  }

  protected boolean validateConnection(Connection conn) {
    try {
      return (!conn.isClosed());
    } catch (SQLException e) {
      if (logger.isDebugEnabled()) {
        logger.debug("AbstractDataSource::validateConnection:exception in validating connection",
            e);
      }
      return false;
    }
  }

  // Get Methods for DataSource Properties
  /**
   * Returns the default user for the database.
   *
   */
  public String getUser() {
    return user;
  }

  /**
   * Returns the default password for the database.
   *
   */
  public String getPassword() {
    return password;
  }

  /**
   * The name of the JDBC driver for the database.
   *
   */
  public String getJDBCDriver() {
    return jdbcDriver;
  }

  // Set Method for Datasource Properties
  /**
   * Sets the default username for the database
   *
   */
  public void setUser(String usr) {
    user = usr;
  }

  /**
   * Sets the default passord for the database user.
   *
   * @param passwd Password for the user
   */
  public void setPassword(String passwd) {
    password = passwd;
  }

  /**
   * Sets the JDBC driver name of the datasource
   *
   */
  public void setJDBCDriver(String confDriver) {
    jdbcDriver = confDriver;
  }

  /**
   * Authenticates the username and password for the database connection.
   *
   * @param clUser The username for the database connection
   * @param clPass The password for the database connection
   */
  public void checkCredentials(String clUser, String clPass) throws SQLException {
    if (clUser == null || !clUser.equals(user) || clPass == null || !clPass.equals(password)) {
      String error =
          String.format(
              "Cannot create a connection with the user, %s, as it doesnt match the existing user named %s, or the password was incorrect.",
              new Object[] {clUser, clPass});
      throw new SQLException(error);
    }
  }

  @Override
  public void close() {
    isActive = false;
  }
}
